#include "precom.h"
#include "CamEye.h"
#include <cmath>

using namespace fmath;
using namespace std;

namespace {
	const float STANDARD_TRANSLATION = 10.f;
}

CamEye::CamEye(GLuint a_width, GLuint a_height)
{
	ChangeToPerspective(a_width, a_height);
	//ChangeToOrtho(a_width, a_height);
	m_axis.identity();
	m_projection.identity();
	m_invProj.identity();
	m_modelNormalZoomRem = m_zoom = 1.0f;
	m_trans.set(0.f, 0.f, -STANDARD_TRANSLATION);
	m_isInvertActual = true;
	m_adjustTrans.set(0.f, 0.f, 0.f);
}

void CamEye::ExpandBox(Box& a_box, float a_margPercent) const
{
	float val = a_margPercent / 100.0f;
	a_box.m_min[0] += a_box.m_min[0] * val;
	a_box.m_min[1] += a_box.m_min[1] * val;
	a_box.m_min[2] += a_box.m_min[2] * val;

	a_box.m_max[0] += a_box.m_max[0] * val;
	a_box.m_max[1] += a_box.m_max[1] * val;
	a_box.m_max[2] += a_box.m_max[2] * val;
}

void CamEye::ChangeToPerspective(GLuint a_width, GLuint a_height)
{
	glViewport(0, 0, a_width, a_height);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(45.0f, (GLfloat)a_width/(GLfloat)a_height, 1.0f, 45.0f);
	glMatrixMode(GL_MODELVIEW);
	ApplyCamera();
}

void CamEye::ChangeToPerspective(const Box& a_box, GLuint a_width, GLuint a_height)
{
	Box bbox = a_box;
	ExpandBox(bbox, 100.0f);
	GLfloat asp = (GLfloat)a_width/(GLfloat)a_height;

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glFrustum(-5.f*asp, 5.f*asp, -5.f, 5.f,
		 2.0f, 50.f);
	glMatrixMode(GL_MODELVIEW);
	
	vector3 size(bbox.m_max[0] - bbox.m_min[0], 
		bbox.m_max[1] - bbox.m_min[1], 
		bbox.m_max[2] - bbox.m_min[2]);
	m_zoom = 10.f / size.x;
	//m_trans.set(0.f, 0.f, -10.f );
	ApplyCamera();
}

void CamEye::AdjustCamToBox(const Box& a_bbox)
{
	float cx, cy, cz;
	cx = a_bbox.m_max[0] - a_bbox.m_min[0];
	cy = a_bbox.m_max[1] - a_bbox.m_min[1];
	cz = a_bbox.m_max[2] - a_bbox.m_min[2];
	cz = max(cx, cz);
	cz = max(cy, cz);
	m_zoom = 6.0f / cz;

	cx = m_zoom * (a_bbox.m_max[0] + a_bbox.m_min[0]) / 2.0f;
	cy = m_zoom * (a_bbox.m_max[1] + a_bbox.m_min[1]) / 2.0f;
	cz = m_zoom * (a_bbox.m_max[2] + a_bbox.m_min[2]) / 2.0f;
	m_projection *= TranslateMatrix44(m_adjustTrans.x, m_adjustTrans.y, m_adjustTrans.z);
	//m_projection.col[3][0] = m_projection.col[3][1] = m_projection.col[3][2] = 0.0f;
	m_adjustTrans.set(cx, cy, cz);
	m_projection *= TranslateMatrix44(-cx, -cy, -cz);
	m_modelNormalZoomRem = m_zoom;
	ApplyCamera();
}

void CamEye::ChangeToOrtho(GLuint a_width, GLuint a_height)
{ 
	glViewport(0, 0, a_width, a_height);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	GLfloat asp = (GLfloat)a_width/(GLfloat)a_height;
	gluPerspective(90.0f,asp,1.0f,100.0f);
	//gluOrtho2D(0, a_width, 0, a_height);
	//glTranslatef(0.f, -a_height, 0.f);
	glMatrixMode(GL_MODELVIEW);
	ApplyCamera();
}

void CamEye::RotateBy(float a_angle, unsigned int a_axisInd)
{
	vector3 axis(m_axis[a_axisInd]);
	m_projection = RotateRadMatrix44(axis, a_angle) * m_projection;
	m_isInvertActual = false;
}

matrix44& CamEye::GetInvertProj()
{
	if(m_isInvertActual) return m_invProj;
	m_invProj = m_projection;
	m_invProj[0][3] = m_invProj[1][3] = m_invProj[2][3] = 0.f;
	m_invProj.invert();
	m_isInvertActual = true;
	return m_invProj;
}

void CamEye::ApplyCamera() 
{
	//glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glTranslatef(m_trans.x, m_trans.y, m_trans.z);
	glMultMatrixf((float*)&m_projection);
	glScalef(m_zoom, m_zoom, m_zoom);
}

void CamEye::DrawAxis()
{
	vector4 ax;
	matrix44 tmp = m_projection;

	glPushMatrix();
	glLoadIdentity();
	glTranslatef(-3.0f, -3.0f, -10.f);

	glBegin(GL_LINES);
	glColor3f(1.0f, 0.5f, 0.0f);
	glVertex3f(0.0f, 0.0f, 0.0f);
	ax = .5f * tmp[0];
	glVertex3fv((float*)&ax);

	glColor3f(0.0f, 1.0f, 0.5f);
	glVertex3f(0.0f, 0.0f, 0.0f);
	ax = .5f * tmp[1];
	glVertex3fv((float*)&ax);

	glColor3f(0.5f, 0.0f, 1.0f);
	glVertex3f(0.0f, 0.0f, 0.0f);
	ax = .5f * tmp[2];
	glVertex3fv((float*)&ax);
	
	glEnd();
	glPopMatrix();
}